package edu.hawaii.senin.iclouds.view;

import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.BevelBorder;

import net.miginfocom.swing.MigLayout;
import edu.hawaii.senin.iclouds.basics.Reaction;
import edu.hawaii.senin.iclouds.basics.Specie;
import edu.hawaii.senin.iclouds.simple.Cloud;
import edu.hawaii.senin.iclouds.util.CloudPhysicalParameters;
import edu.hawaii.senin.iclouds.util.CloudPopulation;
import edu.hawaii.senin.iclouds.util.CloudReactions;

/**
 * Provides a graphical user interface to the traffometer sim project.
 * 
 * @author Pavel Senin
 * 
 */
public class View implements Observer {

  /**
   * Main panel for the GUI.
   */
  private JFrame frame = new JFrame("iClouds Demo v. 1.0");

  // traffobase configuration
  private JPanel mainPanel = new JPanel();
  private JLabel configLabel = new JLabel("Config used:");
  private JTextField configField = new JTextField("");
  private JLabel logLabel = new JLabel("Logging into:");
  private JTextField logField = new JTextField("");

  // tabbed config panel
  private JTabbedPane tabbedConfigPane = new JTabbedPane();

  private JPanel cloudTabbedPane = new JPanel();
  private JLabel cloudTemperatureLabel = new JLabel("Temperature:");
  private JTextField cloudTemperatureField = new JTextField("");
  private JLabel cloudDensityLabel = new JLabel("Density:");
  private JTextField cloudDensityField = new JTextField("");
  private JLabel cloudGrainSizeLabel = new JLabel("Grain size:");
  private JTextField cloudGrainSizeField = new JTextField("");
  private JLabel cloudAccretionStepLabel = new JLabel("Accretion step:");
  private JTextField cloudAccretionStepField = new JTextField("");
  private JLabel cloudIterationLimitLabel = new JLabel("Iteration limit:");
  private JTextField cloudIterationLimitField = new JTextField("");

  private JPanel populationTabbedPane = new JPanel();
  private JTextArea populationTextArea = new JTextArea();
  JScrollPane populationTextPane = new JScrollPane(populationTextArea,
      JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

  private JPanel reactionsTabbedPane = new JPanel();
  private JTextArea reactionsTextArea = new JTextArea();
  JScrollPane reactionsTextPane = new JScrollPane(reactionsTextArea,
      JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

  // buttons pane configuration
  private JPanel buttonsPane = new JPanel();
  private JButton loadConfigButton = new JButton("Load config");
  private JButton saveLogButton = new JButton("Select log");
  private JButton runSimulationButton = new JButton("RunSimulation");
  private JButton stopSimulationButton = new JButton("Stop that!");

  // log panel configuration
  private JTextArea logTextArea = new JTextArea();
  JScrollPane logTextPane = new JScrollPane(logTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
      JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
  private StringBuffer logText = new StringBuffer(10000);

  /**
   * Creates the layout of the GUI window. Uses the MigLayout manager to simplify the display.
   */
  private void configureGUI() {

    // set look and fill
    JFrame.setDefaultLookAndFeelDecorated(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // set the layout for the application
    MigLayout mainFrameLayout = new MigLayout("", "[fill,grow,center]",
        "[fill]10[fill,grow]10[fill]10[fill,grow]");
    this.frame.getContentPane().setLayout(mainFrameLayout);

    // base display panel
    this.frame.getContentPane().add(mainPanel, "wrap");
    mainPanel.setBorder(BorderFactory.createEtchedBorder(BevelBorder.LOWERED));
    MigLayout configurationLayout = new MigLayout("fillx",
        "[left]rel[grow,fill]unrel[left]rel[grow,fill]", "[]10[]10[]");
    this.mainPanel.setLayout(configurationLayout);
    this.mainPanel.add(new JLabel("General iCluds settings:"), "span 4,wrap");
    this.mainPanel.add(configLabel, "");
    this.mainPanel.add(configField, "span 3, grow, wrap");
    configField.setEditable(false);
    this.mainPanel.add(logLabel, "");
    this.mainPanel.add(logField, "span 3, grow, wrap");
    logField.setEditable(false);

    // tabbed configuration pane
    this.frame.getContentPane().add(tabbedConfigPane, "wrap");

    MigLayout cloudTabbedLayout = new MigLayout("fillx", "[left]20[grow,fill]", "");
    cloudTabbedPane.setLayout(cloudTabbedLayout);
    tabbedConfigPane.addTab("Cloud parameters", null, cloudTabbedPane, null);
    this.cloudTabbedPane.add(cloudTemperatureLabel, "");
    this.cloudTabbedPane.add(cloudTemperatureField, "wrap");
    this.cloudTabbedPane.add(cloudDensityLabel, "");
    this.cloudTabbedPane.add(cloudDensityField, "wrap");
    this.cloudTabbedPane.add(cloudGrainSizeLabel, "");
    this.cloudTabbedPane.add(cloudGrainSizeField, "wrap");
    this.cloudTabbedPane.add(cloudAccretionStepLabel, "");
    this.cloudTabbedPane.add(cloudAccretionStepField, "wrap");
    this.cloudTabbedPane.add(cloudIterationLimitLabel, "");
    this.cloudTabbedPane.add(cloudIterationLimitField, "wrap");

    tabbedConfigPane.addTab("Population", null, populationTabbedPane, null);
    MigLayout populationPaneLayout = new MigLayout("", "[grow,fill]", "[grow,fill]");
    populationTabbedPane.setLayout(populationPaneLayout);
    populationTabbedPane.add(populationTextPane, "height :100:");
    this.populationTextArea.setEditable(false);
    this.populationTextArea.append("");
    this.populationTextArea.setCaretPosition(this.populationTextArea.getDocument().getLength());
    populationTextPane.setBorder(BorderFactory.createEtchedBorder(BevelBorder.LOWERED));
    populationTextPane.setAutoscrolls(false);

    tabbedConfigPane.addTab("Reactions", null, reactionsTabbedPane, null);
    MigLayout reactionsPaneLayout = new MigLayout("", "[grow,fill]", "[grow,fill]");
    reactionsTabbedPane.setLayout(reactionsPaneLayout);
    reactionsTabbedPane.add(reactionsTextPane, "height :100:");
    this.reactionsTextArea.setEditable(false);
    this.reactionsTextArea.append("");
    this.reactionsTextArea.setCaretPosition(this.reactionsTextArea.getDocument().getLength());
    reactionsTextPane.setBorder(BorderFactory.createEtchedBorder(BevelBorder.LOWERED));
    reactionsTextPane.setAutoscrolls(false);

    // all buttons pane
    this.frame.getContentPane().add(buttonsPane, "wrap");
    MigLayout buttonsPaneLayout = new MigLayout("", "[grow,fill]20[grow,fill]20[grow,fill]",
        "[grow,fill]10[grow,fill]10[grow,fill]");
    this.buttonsPane.setLayout(buttonsPaneLayout);
    this.frame.getContentPane().add(buttonsPane, "wrap");
    buttonsPane.setBorder(BorderFactory.createEtchedBorder(BevelBorder.LOWERED));
    // add load config button
    buttonsPane.add(loadConfigButton, "");
    // add save log button
    buttonsPane.add(saveLogButton, "");

    buttonsPane.add(runSimulationButton, "");
    // add stop button
    buttonsPane.add(stopSimulationButton, "wrap");

    // adding logging panel
    this.frame.getContentPane().add(logTextPane, "height :100:");
    this.logTextArea.setEditable(false);
    this.logTextArea.append("running Traffosim gold Demo v. 2.1\n");
    this.logTextArea.setCaretPosition(this.logTextArea.getDocument().getLength());
    this.frame.getContentPane().add(logTextPane, "height :100:");
    logTextPane.setBorder(BorderFactory.createEtchedBorder(BevelBorder.LOWERED));
    logTextPane.setAutoscrolls(true);
    this.logText.append("iClouds demo v 1.0 logging:\n");

    // Show frame
    this.frame.pack();
    this.frame.setVisible(true);
  }

  /**
   * Creates an instance of the viewer.
   */
  public View() {
    // does nothing
  }

  /**
   * Obtains this Frame.
   * 
   * @return the Frame.
   */
  public JFrame getJFrame() {
    return this.frame;
  }

  /**
   * Adds the specified load listener.
   * 
   * @param listener the listener to be added.
   */
  public void addLoadListener(ActionListener listener) {
    loadConfigButton.addActionListener(listener);
  }

  /**
   * Adds the log select button listener.
   * 
   * @param listener the listener to be added.
   */
  public void addLogListener(ActionListener listener) {
    saveLogButton.addActionListener(listener);
  }

  /**
   * Adds the RUN listener.
   * 
   * @param listener the listerner to be added.
   */
  public void addRunListener(ActionListener listener) {
    runSimulationButton.addActionListener(listener);
  }

  /**
   * Adds the STOP button listener.
   * 
   * @param listener the listener to be added.
   */
  public void addStopListener(ActionListener listener) {
    stopSimulationButton.addActionListener(listener);
  }

  // /**
  // * Adds the specified real listener.
  // *
  // * @param listener the listener to be added.
  // */
  // public void addRealListener(ActionListener listener) {
  // runRealTimeButton.addActionListener(listener);
  // }
  //
  // /**
  // * Adds the specified load test button listener.
  // *
  // * @param listener the listener to be added.
  // */
  // public void addLoadTestListener(ActionListener listener) {
  // this.runLoadTestButton.addActionListener(listener);
  // }
  //
  // /**
  // * Adds the specified load test button listener.
  // *
  // * @param listener the listener to be added.
  // */
  // public void addDemoListener(ActionListener listener) {
  // this.send10DumbPacketsButton.addActionListener(listener);
  // }

  // /**
  // * Adds the specified metropolis listener.
  // *
  // * @param listener the listener to be added.
  // */
  // public void addMetropolisListener(ActionListener listener) {
  // runLoadTestButton.addActionListener(listener);
  // }

  /**
   * Adds the specified stop listener.
   * 
   * @param listener the listener to be added.
   */
  public void addStopSimulationListener(ActionListener listener) {
    stopSimulationButton.addActionListener(listener);
  }

  // /**
  // * Adds the specified mapopolis listener.
  // *
  // * @param listener the listener to be added.
  // */
  // public void addMapopolisConverter(ActionListener listener) {
  // runMapopolisConverterButton.addActionListener(listener);
  // }
  //
  // /**
  // * Adds the specified transfer button listener.
  // *
  // * @param listener the listener to be added.
  // */
  // public void addTransferPointsListener(ActionListener listener) {
  // transferPointsButton.addActionListener(listener);
  // }

  /**
   * Shows the GUI.
   */
  public void showGUI() {
    // Schedule a job for the event-dispatching thread:
    // creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        configureGUI();
      }
    });
  }

  /**
   * Runs the threads.
   */
  public void run() {
    // empty

  }

  /**
   * Adds a message to the log.
   * 
   * @param str the message to be added to the log.
   */
  public void addToLog(String str) {
    if (str.charAt(str.length() - 1) != '\n') {
      str = str.concat("\n");
    }
    this.logTextArea.append(str);
    this.logTextArea.setCaretPosition(this.logTextArea.getDocument().getLength());
  }

  /**
   * Reports selected base host.
   * 
   * @return host name.
   */
  public String getHostName() {
    return this.configField.getText();
  }

  /**
   * Updates the status of the observable.
   * 
   * @param arg0 the observable item.
   * @param arg1 an object used in updating the status.
   * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
   */
  public void update(Observable o, Object arg) {
    if ((o instanceof Cloud) && (arg instanceof String)) {
      String str = arg.toString();
      if (str.charAt(str.length() - 1) != '\n') {
        str = str.concat("\n");
      }
      this.logTextArea.append(str);
      this.logTextArea.setCaretPosition(this.logTextArea.getDocument().getLength());
    }
    if ((o instanceof Cloud) && (arg instanceof CloudPhysicalParameters)) {
      CloudPhysicalParameters par = (CloudPhysicalParameters) arg;
      cloudTemperatureField.setText(String.valueOf(par.getTemperature()));
      cloudDensityField.setText(String.valueOf(par.getDensity()));
      cloudGrainSizeField.setText(String.valueOf(par.grainSize()));
      cloudAccretionStepField.setText(String.valueOf(par.getAccretionStep()));
      cloudIterationLimitField.setText(String.valueOf(par.getIterationsLimit()));
      this.cloudTabbedPane.updateUI();
      this.cloudTabbedPane.repaint();
    }
    if ((o instanceof Cloud) && (arg instanceof CloudPopulation)) {
      CloudPopulation pop = (CloudPopulation) arg;
      for (Specie s : pop) {
        this.populationTextArea.append(s.toText());
        this.populationTextArea.setCaretPosition(this.populationTextArea.getDocument().getLength());
      }
    }
    if ((o instanceof Cloud) && (arg instanceof CloudReactions)) {
      CloudReactions rStore = (CloudReactions) arg;
      for (Reaction r : rStore) {
        this.reactionsTextArea.append(r.toText());
        this.reactionsTextArea.setCaretPosition(this.populationTextArea.getDocument().getLength());
      }
    }

  }

  /**
   * Sets the config path label.
   * 
   * @param absolutePath config path.
   */
  public void setConfigName(String absolutePath) {
    this.configField.setText(absolutePath);
    this.mainPanel.updateUI();
    this.mainPanel.repaint();
  }

  public CloudPhysicalParameters getPhysicalParameters() {
    Double temperature = Double.valueOf(this.cloudTemperatureField.getText());
    Double density = Double.valueOf(cloudDensityField.getText());
    Double grainSize = Double.valueOf(cloudGrainSizeField.getText());
    Integer accretionStep = Integer.valueOf(cloudAccretionStepField.getText());
    Integer iterationsLimit = Integer.valueOf(cloudIterationLimitField.getText());

    return new CloudPhysicalParameters(temperature, density, 1, grainSize, iterationsLimit,
        accretionStep);
  }

  public void setLogName(String absolutePath) {
    this.logField.setText(absolutePath);
    this.mainPanel.updateUI();
    this.mainPanel.repaint();
  }

  // /**
  // * Reports selected base port.
  // *
  // * @return port number.
  // */
  // public String getPortName() {
  // return this.portTextField.getText();
  // }

  // public TreeMap<String, String> getDynamicParameters() {
  // TreeMap<String, String> res = new TreeMap<String, String>();
  // // private long dynamicStartTime;
  // // private long dynamicEndTime;
  // // private Integer dynamicCarsNum;
  // // private Integer dynamicCarsSpeed;
  // // private Integer dynamicCarsStDev;
  // // private Integer dynamicRandomSeed;
  // //
  // // private JTextField numberOfCarsField = new JTextField("10000");
  // // private JTextField meanSpeedField = new JTextField("35");
  // // private JTextField standardDevField = new JTextField("15");
  // // private JTextField startTimeField = new JTextField("10:00 am");
  // // private JTextField endTimeField = new JTextField("11:00 am");
  // // private JTextField seedField = new JTextField("37");
  // res.put("dynamicStartTime", startTimeField.getText());
  // res.put("dynamicEndTime", endTimeField.getText());
  // res.put("dynamicCarsNum", numberOfCarsField.getText());
  // res.put("dynamicCarsSpeed", meanSpeedField.getText());
  // res.put("dynamicCarsStDev", standardDevField.getText());
  // res.put("dynamicRandomSeed", seedField.getText());
  // return res;
  // }
}
